package com.ymx.darling.session.setting.relation;

import com.ymx.darling.bean.entity.BeanInfo;
import com.ymx.darling.bean.entity.BeanInfoManager;
import com.ymx.darling.exception.DarlingException;
import com.ymx.darling.exception.EntityException;
import com.ymx.darling.exception.ExceptionFactory;
import com.ymx.darling.executor.mapping.relation.RelationFieldInfo;
import com.ymx.darling.util.ObjectUtil;

import java.util.List;

/**
 * 构建ResultEntity
 * @author 爱Java的小于
 */
public class RelationEntityBuilder {
    /**
     * 创建RelationEntity
     * @param module module
     * @param relationNodes 节点
     * @return RelationEntity
     */
    public RelationEntity createRelationEntity(Class<?> module, List<RelationNode> relationNodes) {
        try {
            return this.doCreateRelationEntity(module, relationNodes);
        } catch (DarlingException e) {
            throw ExceptionFactory.wrapException("构建Relation查询结构时抛出异常", e);
        }
    }

    /**
     * 创建RelationEntity
     * @param module module
     * @param relationNodes 关联节点
     * @return RelationEntity
     */
    public RelationEntity doCreateRelationEntity(Class<?> module, List<RelationNode> relationNodes) {
        BeanInfo beanInfo = BeanInfoManager.getBeanInfo(module);
        RelationEntity resultRootEntity = new RelationEntity(beanInfo);
        relationNodes.forEach(relation -> {
            String relationPropertyName = relation.getFieldName();
            RelationFieldInfo relationPropertyInfo = BeanInfoManager.getRelationFieldInfo(module, relationPropertyName);

            // 判断关联字段是否存在
            if (relationPropertyInfo == null) {
                throw new EntityException(String.format("'%s.%s'不是一个合法的级联字段或字段不存在", module.getName(), relationPropertyName));
            }
            // 判断关联字段是否配置了Relation****注解
            if (relationPropertyInfo.getRelationLoader() == null) {
                throw new EntityException(String.format("字段'%s.%s'未配置@Relation***相关注解", module.getName(), relationPropertyName));
            }
            // 获取关联字段的类型
            Class<?> relationType = relationPropertyInfo.getFieldType();
            // 判断关联字段是否为实体类或darling支持的值类型
            if (!(BeanInfoManager.isRegister(relationType) || ObjectUtil.isSimpleType(relationType))) {
                throw new EntityException(String.format("字段'%s.%s'的关联字段类型'%s'不是实体类或darling支持的值类型", module.getName(), relationPropertyName, relationType.getName()));
            }

            // 判断关联字段的类型为实体类
            if (BeanInfoManager.isRegister(relationType)) {
                // 判断不能同时出现
                // 构建级联属性的子节点
                BeanInfo nodeBeanInfo = BeanInfoManager.getBeanInfo(relationType);
                RelationEntity resultNodeEntity = new RelationEntity(nodeBeanInfo);
                // 判断当前子节点定义了深度且子节点类型中存在同名的子节点
                if (relation.hasDepth()) {
                    resultNodeEntity.setDepth(relation.getDepth());
                    boolean hasResultDepthNode = false;
                    for (RelationFieldInfo nestedFieldInfo : nodeBeanInfo.getRelationFieldInfoList()) {
                        if (nestedFieldInfo.getFieldName().equals(relation.getFieldName())) {
                            hasResultDepthNode = true;
                            break;
                        }
                    }
                    if (!hasResultDepthNode) {
                        throw new EntityException("不能为属性"+ relation.getFieldName()+"设置查询深度,在类"+relationType.getName()+"中不存在该属性值");
                    }
                }
                resultNodeEntity.setRelationFieldInfo(relationPropertyInfo);
                // 判断node存在级联子节点 那么构建
                if (relation.hasNode()) {
                    RelationEntity tmpRelationEntity = this.doCreateRelationEntity(relationType, relation.getNodeList());
                    tmpRelationEntity.setRelationFieldInfo(resultNodeEntity.getRelationFieldInfo());
                    resultNodeEntity = tmpRelationEntity;
                }
                resultRootEntity.addResultEntity(resultNodeEntity); // 级联属性节点的数据放入主节点中

            // 判断关联字段类型为值类型
            } else {
                Class<?> targetClass = relationPropertyInfo.getRelationLoader().getTargetClass();
                if (relation.hasDepth()) {
                    throw new EntityException(String.format("字段'%s'类型为值类型时不能设置查询深度", relationPropertyName));
                }
                if (relation.hasNode()) {
                    throw new EntityException(String.format("字段'%s'类型为值类型时不能设置级联属性", relationPropertyName));
                }
                BeanInfo targetBeanInfo = BeanInfoManager.getBeanInfo(targetClass);
                RelationEntity relationEntity = new RelationEntity(targetBeanInfo);
                relationEntity.setRelationFieldInfo(relationPropertyInfo);
                resultRootEntity.addResultEntity(relationEntity);
            }
        });
        return resultRootEntity;
    }
}
